'''
基于预测准确率计算的属性选择法
1使用xgboost自带的指标重要度评估函数，得到指标重要性
2从零开始逐个添加属性，带来提升则保留指标
@author：fengye
'''
from itertools import count
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from ML import NN_model,xgb_model


# 计算使用所有属性得到准确率均值、方差和指标重要性
def total_mean(all_data):
    '''func:计算使用所有属性得到准确率均值、方差和指标重要性'''
    all_attribute_mean, all_attribute_square,index_importance = xgb_model(all_data)
    print('预测总体均值：')
    print(all_attribute_mean)
    print('预测总体方差：')
    print(all_attribute_square)
    print('xgb指标重要性：')
    print(index_importance)
               
    return all_attribute_mean, all_attribute_square,index_importance

# 指标重要性排序
def index_importance_ranking(index_importance):
    '''根据指标重要性进行排序'''
    index_count = len(index_importance)
    index = list(range(0,index_count))
    for i in range(0, index_count):
        for j in range(i+1, index_count):
            if index_importance[i] < index_importance[j]:
                index_importance[i], index_importance[j] = index_importance[
                    j],index_importance[i]
                index[i], index[j] = index[j], index[i]
    print('index rank:', index)
    print('index importance rank:', index_importance) #降序排序，指标重要性递减
    
    return index, index_importance


def lifting_level_opt(all_data, remain_label):
    '''从零开始逐个添加指标，保留是预测准确度上升的指标'''
    acc = [0]  # 根据影响系数添加不同属性的精确度
    best_attributes = []  # 保留指标
    index_important = remain_label.copy()   #指标重要性

    all_data_df = pd.DataFrame(data=all_data)
    data_result = all_data_df.iloc[:, -1]  # 数据标签列
   
    index_number = len(index_important)   #指标个数
    for i in range(index_number):    #保留指标在位置i上的添加情况
        #保留影响系数排名前n的指标
        if i <= 0:    
            best_attributes.append(index_important[i])
            data_attributes = all_data_df.iloc[:, best_attributes]  # 每次添加指标后特征数据
            print(data_attributes[:5])
            data_add = pd.concat([data_attributes, data_result], axis=1)
            data_add = np.array(data_add)  # 转换为numpy数组放入模型中
            
            add_attribute_mean, _, _ = xgb_model(data_add)  # 添加某属性后的准确度均值
            acc.append(add_attribute_mean)
            continue

        best_reduce = False  #终止循环条件
        for j in range(i, index_number):  #尝试index_importance[i,index_number]添加到保留指标i的情况
            best_attributes.append(index_important[j])
            data_attributes = all_data_df.iloc[:, best_attributes]
            print(data_attributes[:5])
            data_add = pd.concat([data_attributes, data_result], axis=1)
            data_add = np.array(data_add)  # 转换为numpy数组放入模型中
            
            add_attribute_mean, _, _ = xgb_model(data_add)  # 添加某属性后的准确度均值
            # 带来模型准确率提升则保留此指标
            if add_attribute_mean > acc[-1]:
                acc.append(add_attribute_mean)
                index_important[i], index_important[j] = index_important[j], index_important[i]
                #下次循环：保留指标在位置i+1上的添加情况
                break
            # 未准确率带来提升，则不保留此指标
            best_attributes.pop()

            if j == index_number:  #尝试到最后一个指标也没带来准确率提升，达成结束循环条件
                best_reduce = True

        if best_reduce == True:
            break

    len_attributes = len(acc)
    x = list(range(1, len_attributes))
    y = acc[1:]
    plt.plot(x, y)
    plt.ylim(0, 80)  # 限定y轴范围
    plt.xlabel('加入属性个数')
    plt.ylabel('预测准确率/%')
    plt.title('根据影响系数前向递归的预测准确率变化曲线')
    plt.rcParams['font.sans-serif'] = ['Simhei']  # 生成图片显示中文
    plt.savefig('./result/准确率提升曲线opt.png')
    
    return best_attributes, acc[1:]



if __name__ == "__main__":
    import pandas as pd
    import numpy as np

    data_path =r'./data/trainStand.xlsx'
    data = pd.read_excel(data_path)
    data = data.fillna(data.mean())
    data = np.array(data)  # nt#也可使用data.value()
    file_name = open('./result/coding_result_opt.txt','w+',encoding='utf-8')    #保存运行结果
    # 总体均值与方差
    all_attribute_mean, all_attribute_square, index_importance= total_mean(data.copy())  
    # 按照指标重要性进行排序
    index_rank, index_importance = index_importance_ranking(index_importance.copy())
    # 计算属性的提升系数（改）
    best_attributes, acc = lifting_level_opt(data.copy(), index_rank.copy())
    print('最佳属性组合：')
    print(best_attributes)

    #保存运行结果
    file_name.write('总体均值与方差：\n')
    file_name.write(str(all_attribute_mean) + '  ' + str(all_attribute_square))   
    file_name.write('\n-------------------------------\n')
    file_name.write('指标按重要性排序：\n')
    file_name.write(str(index_rank) + '\n')
    file_name.write('指标重要性：\n')
    file_name.write(str(index_importance))   
    file_name.write('\n------------------------------\n')
    file_name.write('最佳属性组合：\n')
    file_name.write(str(best_attributes) + '\n')
    file_name.write('最佳属性组合对应的准确度变化：\n')
    file_name.write(str(acc) + '\n')
    file_name.close()